﻿using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;

namespace KLTP2UI
{
	class ParseController
	{
		private byte[] _parseArray, _cacheArray;

		private bool _isProcessing;

		private readonly string _file;
		private Thread _parseThread, _readThread;
		private const int MEGABYTE = 1048576;
		private uint _unpInt, _unpExt, _blockLen;
		private IntPtr _ptr;


		public ParseController(string logfile)
		{
			_file = logfile;
			_parseArray = new byte[MEGABYTE];
			_cacheArray = new byte[MEGABYTE];
			_isProcessing = false;
			_unpInt = 0;
			_unpExt = _unpInt;
			Processed = _unpInt;
		}

		~ParseController()
		{
			StopProcessing();
		}

		private void StartProcessingInternal()
		{
			_isProcessing = true;
			using (var fileStream = new FileStream(_file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
			{
				Length = fileStream.Length;
				while (_isProcessing && Length != fileStream.Position)
				{
					if (Processed > 0)
					{
						_unpInt = DllMem.ParseBack(_ptr, _blockLen);
						fileStream.Seek(-_unpInt, SeekOrigin.Current);
					}

					var read = fileStream.Read(_cacheArray, 0, MEGABYTE);

					if (DllMem.IsThreadJoinable(_parseThread))
						_parseThread.Join();
					
					if(!_isProcessing)
						continue;

					if(_unpInt != _unpExt)
						throw new Exception("Externall call returned different amount of unprocessed data!");

					Processed = fileStream.Position;
					if(Processed > Length)
						Length = fileStream.Length;
					FireProgress();

					var tmp = _parseArray;
					_parseArray = _cacheArray;
					_cacheArray = tmp;
					_ptr = Marshal.UnsafeAddrOfPinnedArrayElement(_parseArray, 0);
				    _blockLen = (uint)read;

					_parseThread = new Thread(Parse) {Name = "Parse thread"};
					_parseThread.Start();
				}
				if (DllMem.IsThreadJoinable(_parseThread))
					_parseThread.Join();
			}
			_isProcessing = false;
			FireProgress();
		}

		private void Parse()
		{
			_unpExt = DllMem.AddBlock(_ptr, _blockLen);
		}

		private void FireProgress()
		{
			if (SomeProgress != null)
				SomeProgress(this, null);
		}


		public long Processed { get; private set; }

		public long Length { get; private set; }


		public void StartProcessing()
		{
			if (_isProcessing)
				return;

			_readThread = new Thread(StartProcessingInternal) {Name="Read thread"};
			_readThread.Start();
		}

		public void StopProcessing()
		{
			if (!_isProcessing)
				return;

			_isProcessing = false;
			WaitProcessingEnds();
		}

		public bool IsProcessing()
		{
			return _isProcessing;
		}

		private void WaitProcessingEnds()
		{
			if (DllMem.IsThreadJoinable(_readThread))
				_readThread.Join();
		}

		
		public event EventHandler SomeProgress;
	}
}
